/*
 *  Copyright 2009 Advanced Industrial Science and Technology (AIST).
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

/*! \file
 *  \brief
 */
#pragma once

// thrust, includes
#include <thrust/graph/graph_traits.hpp>
#include <thrust/graph/graph_selectors.hpp>
#include <thrust/graph/properties.hpp>
#include <thrust/graph/detail/edge.hpp>
#include <thrust/iterator/iterator_traits.h>
#include <thrust/tuple.h>
#include <thrust/mpl/if.hpp>

namespace thrust {
  
  template <
    typename DirectedS
    , typename ParallelS
    , typename VertexProperty
    , typename EdgeProperty
    , typename SizeType
    >
  struct adjacency_list_traits {
    
    typedef typename DirectedS::is_bidir_t    is_bidir;
    typedef typename DirectedS::is_directed_t is_directed;
    typedef typename thrust::mpl::if_<is_bidir
                                      , bidirectional_tag
                                      , typename thrust::mpl::if_<is_directed
                                                                  , directed_tag
                                                                  , undirected_tag>::type
                                      >::type directed_category;
    
    typedef typename ParallelS::is_parallel_t is_parallel;
    typedef typename thrust::mpl::if_<is_parallel
                                      , allow_parallel_edge_tag
                                      , disallow_parallel_edge_tag
                                      >::type edge_parallel_category;
    
    //---------------------------------------
    // vertex_descriptor and edge_descriptor
    typedef SizeType  size_type;
    typedef size_type vertex_descriptor;
    typedef size_type edges_size_type;
    
    typedef detail::edge_desc_impl<directed_category
                                   , vertex_descriptor
                                   , EdgeProperty> edge_descriptor;
  };
}

#include <thrust/graph/detail/adjacency_list.hpp>
namespace thrust {
  
  template <
    typename DirectedS
    , typename ParallelS
    , typename VertexProperty =no_property
    , typename EdgeProperty   =no_property
    , typename SizeType       =unsigned int
    >
  class adjacency_list 
    : public detail::adj_list_gen< adjacency_list<DirectedS
                                                  , ParallelS
                                                  , VertexProperty
                                                  , EdgeProperty
                                                  , SizeType
                                                  > 
                                   , DirectedS
                                   , ParallelS
                                   , VertexProperty
                                   , EdgeProperty
                                   , SizeType
                                   >::type
  {
    
  public:
    typedef VertexProperty vertex_property_type;
    typedef EdgeProperty   edge_property_type;
    typedef SizeType       size_type;
    
  private:
    typedef adjacency_list self;
    typedef typename detail::adj_list_gen<self
                                          , DirectedS
                                          , ParallelS
                                          , VertexProperty
                                          , EdgeProperty
                                          , SizeType
                                          >::type Base;
    
  public:
    __host__ inline 
    adjacency_list() 
      : Base() {}
    
    __host__ inline 
    adjacency_list( size_type num_vertices)
      : Base( num_vertices) {}

    __host__ inline 
    adjacency_list( size_type num_vertices, size_type num_edges)
      : Base( num_vertices, num_edges) {}
  };
  
  
#define ADJ_LIST_PARAS DirectedS,ParallelS,VertexProperty,EdgeProperty,SizeType
  
  template <typename Directed
            , typename Vertex
            , typename DirectedS
            , typename ParallelS
            , typename VertexProperty
            , typename EdgeProperty
            , typename SizeType>
  __host__ __device__
  inline Vertex
  source( const detail::edge_base<Directed,Vertex>& e, 
          const adjacency_list<ADJ_LIST_PARAS>&)
  {
    return e.m_source;
  }

  template <typename Directed
            , typename Vertex
            , typename DirectedS
            , typename ParallelS
            , typename VertexProperty
            , typename EdgeProperty
            , typename SizeType>
  __host__ __device__
  inline Vertex
  target( const detail::edge_base<Directed,Vertex>& e,
          const adjacency_list<ADJ_LIST_PARAS>&)
  {
    return e.m_target;
  }

}
